using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Web;
using System.Web.Mail;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;

namespace Halide
{
	/// <summary>
	/// The Secure class contains methods and properties for performing or evaluating security and authentication related operations.
	/// </summary>
	sealed public class Secure
	{
		private static string _version = "2008.11.12A";

		/// <summary>
		/// Return the version number of the class; read-only.
		/// </summary>
		/// <value>
		/// A string with the version number of the class.
		/// </value>
		public static string Version
		{
			get
			{
				return _version;
			}
		}

		/// <summary>
		/// No constructor is necessary.
		/// </summary>
		private Secure()
	    {
	    }

		/// <summary>
		/// The secret key to use for the symmetric algorithm.
		/// You should change these numbers for your individual use
		/// by adding an AppSetting in the web.config named BaseKey
		/// of 24 numbers from 0-255, as:
		/// 151,4,109,42,135,99,67,82,242,233,16,200,9,83,196,178,56,74,90,36,206,129,81,229
		/// </summary>
		public static byte[] basekey1
		{
			get
			{
				byte[] _basekey1 = { 151, 4, 109, 42, 135, 99, 67, 82, 242, 233, 16, 200, 9, 83, 196, 178, 56, 74, 90, 36, 206, 129, 81, 229 };
				string key = (ConfigurationManager.AppSettings["BaseKey"] == null ? "" : ConfigurationManager.AppSettings["BaseKey"].ToString());

				try
				{
					if (!String.IsNullOrEmpty(key))
					{
						string[] bArray = key.Split(',');

						if (_basekey1.Length == bArray.Length)
						{
							for (int x = 0; x < bArray.Length; x++)
							{
								_basekey1[x] = Convert.ToByte(bArray[x].Trim());
							}
						}
					}
				}

				catch
				{
					HttpContext.Current.Response.Write("<p>*****" + key + "-----" + _basekey1.ToString() + "</p>");
				}

				return _basekey1;
			}
		}

		/// <summary>
		/// The initialization vector to use for the symmetric algorithm.
		/// You should change these numbers for your individual use
		/// by adding an AppSetting in the web.config named BaseKey
		/// of 18 numbers from 0-255, as:
		/// 62,99,180,54,206,210,10,101,6,87,13,3,241,189,176,175,109,217
		/// </summary>
		public static byte[] iv
		{
			get
			{
				byte[] _iv = { 62, 99, 180, 54, 206, 210, 10, 101, 6, 87, 13, 3, 241, 189, 176, 175, 109, 217 };
				string key = (ConfigurationManager.AppSettings["InitVector"] == null ? "" : ConfigurationManager.AppSettings["InitVector"].ToString());

				try
				{
					if (!String.IsNullOrEmpty(key))
					{
						string[] bArray = key.Split(',');

						if (_iv.Length == bArray.Length)
						{
							for (int x = 0; x < bArray.Length; x++)
							{
								_iv[x] = Convert.ToByte(bArray[x].Trim());
							}
						}
					}
				}

				catch
				{
					HttpContext.Current.Response.Write("<p>*****" + key + "-----" + _iv.ToString() + "</p>");
				}
				return _iv;
			}
		}

		/// <summary>
		/// Imports and enums for use with Impersonation functions
		/// </summary>
		/// <param name="principal">User Name.</param>
		/// <param name="authority">Domain Name.</param>
		/// <param name="password">Password.</param>
		/// <param name="logonType">LogonSessionType enum value for type of logon.</param>
		/// <param name="logonProvider">LogonProvider enum value for logon provider.</param>
		/// <param name="token">Output parameter pointer token.</param>
		/// <returns>true or false.</returns>
		[DllImport("advapi32.dll", SetLastError = true)]
		private extern static bool LogonUser(string principal, string authority, string password, LogonSessionType logonType, LogonProvider logonProvider, out IntPtr token);
		
		
		/// <summary>
		/// Close an opened handle.
		/// </summary>
		/// <param name="handle">Handle pointer.</param>
		/// <returns>true or false.</returns>
		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern bool CloseHandle(IntPtr handle);

		/// <summary>
		/// LogonSessionType for LogonUser method.
		/// </summary>
		[type: CLSCompliant(false)]
		public enum LogonSessionType : uint
		{
			/// <summary>
			/// Interactive authentication.
			/// </summary>
			Interactive = 2,

			/// <summary>
			/// Network authentication.
			/// </summary>
			Network,

			/// <summary>
			/// Batch authentication.
			/// </summary>
			Batch,

			/// <summary>
			/// Service authentication.
			/// </summary>
			Service,

			/// <summary>
			/// Network authentication using clear text.
			/// </summary>
			NetworkCleartext = 8,

			/// <summary>
			/// New Credentials.
			/// </summary>
			NewCredentials
		}

		/// <summary>
		/// Logon provider enum for LogonUser method.
		/// </summary>
		[type: CLSCompliant(false)]
		public enum LogonProvider : uint
		{
			/// <summary>
			/// Default platform.
			/// </summary>
			Default = 0, // default for platform (use this!)

			/// <summary>
			/// Windows NT 3.5 style.
			/// </summary>
			WinNT35,     // sends smoke signals to authority

			/// <summary>
			/// Windows NT 4.0 style.
			/// </summary>
			WinNT40,     // uses NTLM

			/// <summary>
			/// Windows 2000 style using Kerberos or NTLM.
			/// </summary>
			WinNT50      // negotiates Kerb or NTLM
		}

		/// <summary>
		/// token pointer used with LogonUser method.
		/// </summary>
		private static IntPtr token = IntPtr.Zero;
		
		/// <summary>
		/// Impersonated context variable.
		/// </summary>
		private static WindowsImpersonationContext impersonatedUser;

	    /// <summary>
		/// Begins impersonation of an authenticated user on the server, so your page will
		/// have non-anonymous permissions (eg. to write files, etc.).
		/// </summary>
		/// <example>
		/// <code>
		/// bool result = Halide.Secure.Impersonate("DomainName", "jsmith", "password");
	    /// </code>
		/// </example>
	    /// <param name="domain">Domain name.</param>
	    /// <param name="userName">User name.</param>
	    /// <param name="password">Password.</param>
		/// <returns>A bool; true if logged on, false if not.</returns>
		public static bool Impersonate(string domain, string userName, string password)
		{
			try
			{
				// Call LogonUser
				bool result = LogonUser(userName, domain, password, LogonSessionType.Interactive, LogonProvider.Default, out token);

				if (result)
				{
					WindowsIdentity id = new WindowsIdentity(token);
					impersonatedUser = id.Impersonate();
				}
				else
				{
					return false;
				}
			}
			catch
			{
				// Prevent any exceptions that occur while the thread is 
				// impersonating from propagating
			}
			return true;
		}

		/// <summary>
		/// Begins impersonation of an authenticated user on the server, so your page will
		/// have non-anonymous permissions (eg. to write files, etc.).
		/// </summary>
		/// <example>
		/// You must have the following in your web.config file to use this function.
		/// <code>
		///	&lt;appSettings&gt;
		///		&lt;add key="ImpIdentity" value="Domain/jsmith;password"/&gt;
		/// &lt;/appSettings&gt;
		/// </code>
		/// Example usage for the method below:
		/// <code>
		/// bool result = Halide.Secure.Impersonate();
		/// </code>
		/// </example>
		/// <returns>A bool; true if logged on, false if not.</returns>
		public static bool Impersonate()
		{
			String domain = string.Empty;
			String userName = string.Empty;
			String password = string.Empty;
			String full = (String)ConfigurationManager.AppSettings["ImpIdentity"];
			String splitter = "/";

			Int32 s1 = full.IndexOf(splitter);
			Int32 s2 = full.IndexOf(@"\");

			if (s1 > 0 && s2 > 0)
			{
				if (s2 < s1)
				{
					splitter = @"\";
				}
			}

			else
			{
				if (s2 > 0)
				{
					splitter = @"\";
				}
			}

			// Access the <appSettings> value
			string[] userAndPassword = full.Split(';');

			// Parse out the domain, username and password
			domain = userAndPassword[0].Substring(0, userAndPassword[0].IndexOf(splitter));
			userName = userAndPassword[0].Substring(userAndPassword[0].IndexOf(splitter)+1);
			password = userAndPassword[1];

			return Impersonate(domain, userName, password);
		}

	    /// <summary>
	    /// Ends impersonation of an authenticated user on the server, reverting back to
		/// an anonymous, default, network service account.
	    /// </summary>
		/// <example>
		/// <code>
		/// bool result = Halide.Secure.UnImpersonate();
		/// </code>
		/// </example>
		/// <returns>Nothing.</returns>
		public static void UnImpersonate()
		{
			// Stop impersonation and revert to the process identity
			if (impersonatedUser != null) impersonatedUser.Undo();

			// Free the token
			if (token != IntPtr.Zero) CloseHandle(token);
		}

	    /// <summary>
	    /// Get the currently logged-in user name (with domain) for the running web page.
	    /// </summary>
		/// <example>
		/// <code>
		/// Response.Write (Halide.Secure.GetIdentity());
		/// </code>
		/// </example>
		/// <returns>String containing the domain and user name currently logged in via the Impersonation method.</returns>
		public static string GetIdentity()
		{
			return WindowsIdentity.GetCurrent().Name;
		}

	    /// <summary>
	    /// Get the currently logged-in user name (with domain) from the client.
	    /// </summary>
		/// <example>
		/// <code>
		/// Response.Write (Halide.Secure.GetCurrentUser());
		/// </code>
		/// </example>
		/// <returns>String containing the domain and user name currently logged in via the web browser's authentication dialog.</returns>
		public static string GetCurrentUser()
		{
			return HttpContext.Current.User.Identity.Name.ToString();
		}

	    /// <summary>
		/// 256 bit string encryption. You may need to use "Server.UrlEncode"
		/// if passing on via a URL. "Server.UrlDecode" should not be necessary
		/// when reading it from a URL. You should change the basekey
		///	and iv value lists to guarantee a unique encryption algorithm.
		/// </summary>
		/// <example>
		/// <code>
		/// string encryptedVar = Halide.Secure.Encrypt("password");
		/// </code>
		/// </example>
		/// <param name="data">String to encrypt.</param>
	    /// <param name="key">Byte key for encrypting the data.</param>
		/// <returns>An encrypted string.</returns>
	    public static string Encrypt(string data, byte[] key)
	    {
	        try
	        {
	            ASCIIEncoding encoder = new ASCIIEncoding();

	            byte[] inputInBytes = encoder.GetBytes(data);


	            TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();

	            ICryptoTransform cryptoTransform = tdesProvider.CreateEncryptor(key, Halide.Secure.iv);


	            MemoryStream encryptedStream = new MemoryStream();
	            CryptoStream cryptStream = new CryptoStream(encryptedStream, cryptoTransform, CryptoStreamMode.Write);


	            cryptStream.Write(inputInBytes, 0, inputInBytes.Length) ;
	            cryptStream.FlushFinalBlock() ;
	            encryptedStream.Position = 0 ;

	            byte[] result = new byte[encryptedStream.Length];
	            encryptedStream.Read(result,0,Convert.ToInt32(encryptedStream.Length));

	            cryptStream.Close();

	            return System.Convert.ToBase64String(result);
	        }
	        catch
	        {
	            return "";
	        }

	    }

		/// <summary>
		/// 256 bit string encryption, encoded for passing on a URL.
		/// Uses the default basekey and iv value list.
		/// </summary>
		/// <example>
		/// <code>
		/// string encryptedVar = Halide.Secure.UrlEncrypt("password");
		/// </code>
		/// </example>
		/// <param name="data">String to encrypt.</param>
		/// <returns>An encrypted string, encoded for passing on a URL.</returns>
		public static string UrlEncrypt(string data)
		{
			return HttpContext.Current.Server.UrlEncode(Encrypt(data));
		}

		/// <summary>
		/// 256 bit string encryption. You may need to use "Server.UrlEncode"
		/// if passing on via a URL, or use the UrlEncrypt method.
		/// Uses the default basekey and iv value list.
		/// </summary>
		/// <example>
		/// <code>
		/// string encryptedVar = Halide.Secure.Encrypt("password");
		/// </code>
		/// </example>
		/// <param name="data">String to encrypt.</param>
		/// <returns>An encrypted string.</returns>
		public static string Encrypt(string data)
		{
			return Encrypt(data, Halide.Secure.basekey1);
		}

	    /// <summary>
	    /// 256 bit string decryption.
	    /// </summary>
		/// <example>
		/// <code>
		/// string decryptedVar = Halide.Secure.Decrypt(encryptedVar, bytekey);
		/// </code>
		/// </example>
		/// <param name="data">String to decrypt.</param>
	    /// <param name="key">Byte key for decrypting the data.</param>
		/// <returns>A decrypted string</returns>
	    public static string Decrypt(string data, byte[] key)
	    {
			string newdata = data.Replace(" ", "+");

	        try
	        {
	            ASCIIEncoding encoder = new ASCIIEncoding();

	            byte[] inputInBytes = System.Convert.FromBase64String(newdata);


	            TripleDESCryptoServiceProvider tdesProvider = new TripleDESCryptoServiceProvider();

	            ICryptoTransform cryptoTransform = tdesProvider.CreateDecryptor(key, Halide.Secure.iv);


	            MemoryStream decryptedStream = new MemoryStream();
	            CryptoStream cryptStream = new CryptoStream(decryptedStream, cryptoTransform, CryptoStreamMode.Write);


	            cryptStream.Write(inputInBytes, 0, inputInBytes.Length) ;
	            cryptStream.FlushFinalBlock() ;
	            decryptedStream.Position = 0 ;

	            byte[] result = new byte[decryptedStream.Length];
	            decryptedStream.Read(result,0,Convert.ToInt32(decryptedStream.Length));

	            cryptStream.Close();

	            return encoder.GetString(result);
	        }
	        catch
	        {
	            return "";
	        }

	    }

		/// <summary>
		/// 256 bit string decryption. Uses built-in basekey.
		/// </summary>
		/// <example>
		/// <code>
		/// string decryptedVar = Halide.Secure.Decrypt(encryptedVar);
		/// </code>
		/// </example>
		/// <param name="data">String to decrypt.</param>
		/// <returns>A decrypted string</returns>
		public static string Decrypt(string data)
		{
			return Decrypt(data, Halide.Secure.basekey1);
		}

		/// <summary>
		/// MD5 encodes the passed string.
		/// </summary>
		/// <example>
		/// <code>
		/// string encodedVar = Halide.Secure.MD5String(stringVar);
		/// </code>
		/// </example>
		/// <param name="input">The string to encode.</param>
		/// <returns>An MD5 encoded string.</returns>
		public static string MD5String(string input)
		{
			// Create a new instance of the MD5CryptoServiceProvider object.
			MD5 md5Hasher = MD5.Create();

			// Convert the input string to a byte array and compute the hash.
			byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

			// Create a new Stringbuilder to collect the bytes
			// and create a string.
			StringBuilder sBuilder = new StringBuilder();

			// Loop through each byte of the hashed data 
			// and format each one as a hexadecimal string.
			for (int i = 0; i < data.Length; i++)
			{
				sBuilder.Append(data[i].ToString("x2"));
			}

			// Return the hexadecimal string.
			return sBuilder.ToString();
		}

		/// <summary>
		/// Verified a string against the passed MD5 hash.
		/// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Secure.MD5VerifyString(stringVar, hash))...
		/// </code>
		/// </example>
		/// <param name="input">The string to compare.</param>
		/// <param name="hash">The hash to compare against.</param>
		/// <returns>true if the input and the hash are the same, false otherwise.</returns>
		public static bool MD5VerifyString(string input, string hash)
		{
			// Hash the input.
			string hashOfInput = MD5String(input);

			// Create a StringComparer an comare the hashes.
			StringComparer comparer = StringComparer.OrdinalIgnoreCase;

			if (0 == comparer.Compare(hashOfInput, hash))
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		/// <summary>
		/// Base64 encodes a string.
		/// </summary>
		/// <example>
		/// <code>
		/// string encodedVar = Halide.Secure.Base64StringEncode(stringVar);
		/// </code>
		/// </example>
		/// <param name="input">A string to encode.</param>
		/// <returns>A base64 encoded string.</returns>
		public static string Base64StringEncode(string input)
		{
			byte[] encbuff = System.Text.Encoding.UTF8.GetBytes(input);
			return Convert.ToBase64String(encbuff);
		}

		/// <summary>
		/// Base64 decodes a string.
		/// </summary>
		/// <example>
		/// <code>
		/// string decodedVar = Halide.Secure.Base64StringDecode(stringVar);
		/// </code>
		/// </example>
		/// <param name="input">A base64 encoded string.</param>
		/// <returns>A decoded string.</returns>
		public static string Base64StringDecode(string input)
		{
			byte[] decbuff = Convert.FromBase64String(input);
			return System.Text.Encoding.UTF8.GetString(decbuff);
		}

		private static string EncryptDecrypt(string textToEncrypt)
		{
			int key = 227;
			StringBuilder inSb = new StringBuilder(textToEncrypt);
			StringBuilder outSb = new StringBuilder(textToEncrypt.Length);
			char c;

			for (int i = 0; i < textToEncrypt.Length; i++)
			{
				c = inSb[i];
				c = (char)(c ^ key);
				outSb.Append(c);
			}

			return outSb.ToString();
		}

		/// <summary>
		/// Encrypt a string using a simple XOR and Base64 Encode.
		/// Ideal for passing parameters on a URL. If you need strong
		/// encryption, do not use this method.
		/// </summary>
		/// <param name="input">String value to encrypt.</param>
		/// <returns>Encrypted string.</returns>
		public static string TinyEncrypt(string input)
		{
			return (Base64StringEncode(EncryptDecrypt(input)));
		}

		/// <summary>
		/// Decrypt a string encrypted with TinyEncrypt.
		/// Ideal for passing parameters on a URL. If you need strong
		/// encryption, do not use this method.
		/// </summary>
		/// <param name="input">Encrypted string value to decrypt.</param>
		/// <returns>Decrypted string.</returns>
		public static string TinyDecrypt(string input)
		{
			return (EncryptDecrypt(Base64StringDecode(input)));
		}

		// Outer Identify class
    }

    // Outer namespace
}